package com.allen.study.common.utils.jwt;

import cn.hutool.core.convert.Convert;
import com.allen.study.common.constant.SecurityConstants;
import com.allen.study.common.exception.CustomRuntimeException;
import com.allen.study.common.utils.redis.RedisUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.function.Function;

/**
 * @ClassName: JwtUtils
 * @Author: AllenSun
 * @Date: 2025/3/7 20:35
 *
 * 全局异常处理器（通常使用 @ControllerAdvice 和 @ExceptionHandler 注解）主要处理控制器方法调用过程中抛出的异常。
 * 要是 JWT 异常在过滤器或者拦截器中抛出，且这些组件不在控制器方法调用链里，全局异常处理器就无法捕获这些异常。
 */
@Component
@Slf4j
public class JwtUtils {
    // 秘钥
    private static final String SECRET_KEY = "123456654321";  // 建议存储于环境变量

    // 过期时间
    private static final long TOKEN_EXPIRATION_MS = 86400000;  // 24小时

    // redis缓存key
    private static final String TOKEN_KEY_PREFIX = "auth:token:";

    @Autowired
    private RedisUtils redisUtils;

    // 从token中提取用户名
    public String extractUsername(String token) {
        // 从token中提取声明，并返回用户名
        return extractClaim(token, Claims::getSubject);
    }

    /**
     * 判断 Token 是否过期
     * @param token JWT Token
     * @return 如果过期返回 true，否则返回 false
     */
    public boolean isTokenExpired2(String token) {
        try {
            Claims claims = parseToken(token);
            Date expiration = claims.getExpiration();
            return expiration.before(new Date());
        } catch (Exception e) {
            return true;
        }
    }

    /**
     * 解析 JWT Token 并获取 Claims
     * @param token JWT Token
     * @return Claims 对象
     */
    public Claims parseToken(String token) {
        return Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody();
    }

    // 从token中提取过期时间
    public Date extractExpiration(String token) {
        // 调用extractClaim方法，传入token和Claims::getExpiration方法引用
        return extractClaim(token, Claims::getExpiration);
    }

    // 从token中提取指定类型的声明
    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        // 从token中提取所有的claims
        final Claims claims = extractAllClaims(token);
        // 使用claimsResolver函数处理claims，并返回结果
        return claimsResolver.apply(claims);
    }

    // 从token中提取所有声明
    public Claims extractAllClaims(String token) {
        // 使用SECRET_KEY解析token，并返回声明
        Claims claims = null;
        try {
            claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
        } catch (SignatureException e) {
            log.info("无效的JWT签名：{}",e.getMessage());
            throw new CustomRuntimeException("无效的JWT签名，请重新登录");
        } catch (MalformedJwtException e) {
            log.info("无效的JWT签名：{}",e.getMessage());
            throw new CustomRuntimeException("无效的JWT签名，请重新登录");
        }

        return claims;
    }

    // 判断token是否过期
    private Boolean isTokenExpired(String token) {
        // 提取token中的过期时间
        return extractExpiration(token).before(new Date());
    }

    /**
     * 根据令牌获取用户标识
     *
     * @param token 令牌
     * @return 用户ID
     */
    public String getUserKey(String token)
    {
        Claims claims = extractAllClaims(token);
        return getValue(claims, SecurityConstants.USER_KEY);
    }

    /**
     * 根据身份信息获取键值
     *
     * @param claims 身份信息
     * @param key 键
     * @return 值
     */
    public String getValue(Claims claims, String key)
    {
        return Convert.toStr(claims.get(key), "");
    }


    /**
     * 根据令牌获取用户标识
     *
     * @param claims 身份信息
     * @return 用户ID
     */
    public String getUserKey(Claims claims)
    {
        return getValue(claims, SecurityConstants.USER_KEY);
    }

    /**
     * 根据身份信息获取用户ID
     *
     * @param claims 身份信息
     * @return 用户ID
     */
    public String getUserId(Claims claims)
    {
        return getValue(claims, SecurityConstants.DETAILS_USER_ID);
    }

    /**
     * 根据令牌获取用户名
     *
     * @param token 令牌
     * @return 用户名
     */
    public String getUserName(String token)
    {
        Claims claims = extractAllClaims(token);
        return getValue(claims, SecurityConstants.DETAILS_USERNAME);
    }

    /**
     * 根据身份信息获取用户名
     *
     * @param claims 身份信息
     * @return 用户名
     */
    public String getUserName(Claims claims)
    {
        return getValue(claims, SecurityConstants.DETAILS_USERNAME);
    }

}
